<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="RDldF" id="RDldF"><span data-lake-id="u52fd729d" id="u52fd729d">典型回答</span></h1>
  <p data-lake-id="uc3de6f3b" id="uc3de6f3b"><strong><span data-lake-id="u1985ae14" id="u1985ae14">​</span></strong><br></p>
  <p data-lake-id="u6b72b3ca" id="u6b72b3ca"><span data-lake-id="ubb71b9c5" id="ubb71b9c5">RPC 是Remote Procedure Call的缩写，译为远程过程调用。要想实现RPC通常需要包含传输协议和序列化协议的实现。</span></p>
  <p data-lake-id="u64ae6fd7" id="u64ae6fd7"><span data-lake-id="uab53f1cd" id="uab53f1cd">​</span><br></p>
  <p data-lake-id="u41de206a" id="u41de206a"><span data-lake-id="uefa558cb" id="uefa558cb">而我们熟知的HTTP，他的中文名叫超文本传输协议，所以他就是一种传输协议。所以，我们可以认为RPC和HTTP并不是同一个维度的两个概念。只不过他们都是可以作为远程调用的，所以经常拿来对比。</span></p>
  <p data-lake-id="ub0b03f8b" id="ub0b03f8b"><span data-lake-id="u5c775e0c" id="u5c775e0c">​</span><br></p>
  <p data-lake-id="u69231815" id="u69231815"><span data-lake-id="u34e3084a" id="u34e3084a">RPC的具体实现上，可以像HTTP一样，基于TCP协议来实现，也可以直接基于HTTP协议实现。</span></p>
  <p data-lake-id="uf453d67a" id="uf453d67a"><span data-lake-id="u6cd16183" id="u6cd16183">​</span><br></p>
  <p data-lake-id="uca884905" id="uca884905"><strong><span data-lake-id="u9c7ee4a2" id="u9c7ee4a2">RPC主要用于公司内部服务之间的互相调用，所以他性能消耗低，传输效率高，服务治理方便。</span></strong></p>
  <p data-lake-id="u4fb760db" id="u4fb760db"><strong><span data-lake-id="u6bde2bd3" id="u6bde2bd3">而HTTP主要用于对外的异构环境，浏览器调用，APP接口调用，第三方接口调用等等。</span></strong></p>
  <p data-lake-id="u8d5edb22" id="u8d5edb22"><span data-lake-id="u3c142fe4" id="u3c142fe4">​</span><br></p>
  <p data-lake-id="u2f8ae86b" id="u2f8ae86b"><br></p>
  <h1 data-lake-id="H17vn" id="H17vn"><span data-lake-id="u7f722dc8" id="u7f722dc8">扩展知识</span></h1>
  <h3 data-lake-id="aff68ab1" id="aff68ab1"><span data-lake-id="uc58974c1" id="uc58974c1">为什么需要远程调用</span></h3>
  <p data-lake-id="u7b2b8254" id="u7b2b8254"><br></p>
  <p data-lake-id="ud0b8fe37" id="ud0b8fe37"><span data-lake-id="u1fe849f2" id="u1fe849f2">如今的大型网站都是分布式部署的。拿一个下单流程来说，可能涉及到物流、支付、库存、红包等多个系统后，而多个系统又是分别部署在不同的机器上的，分别由不同的团队负责。而要想实现下单流程，就需要用到远程调用。</span></p>
  <p data-lake-id="u50a25f03" id="u50a25f03"><br></p>
  <pre lang="java"><code>
下单{
    库存-&gt;减少库存
    支付-&gt;扣款
    红包-&gt;红包抵用
    物流-&gt;生成物流信息
}
</code></pre>
  <p data-lake-id="u1da4317d" id="u1da4317d"><br></p>
  <h3 data-lake-id="kV4Xz" id="kV4Xz"><span data-lake-id="udbb5279d" id="udbb5279d">到底什么是远程过程调用</span></h3>
  <p data-lake-id="uc35ed80b" id="uc35ed80b"><br></p>
  <p data-lake-id="ud33b73f8" id="ud33b73f8"><span data-lake-id="ubd202939" id="ubd202939">RPC 是指计算机 A 上的进程，调用另外一台计算机 B 上的进程，其中 A 上的调用进程被挂起，而 B 上的被调用进程开始执行，当值返回给 A 时，A 进程继续执行。调用方可以通过使用参数将信息传送给被调用方，而后可以通过传回的结果得到信息。而这一过程，对于开发人员来说是透明的。</span></p>
  <p data-lake-id="u8eedb09c" id="u8eedb09c"><br></p>
  <p data-lake-id="u4d6f4da2" id="u4d6f4da2"><span data-lake-id="uc6ecdc1e" id="uc6ecdc1e">就像后厨的例子一样，服务员把菜单传给后厨，厨师告诉备菜师和洗菜师开始工作，然后他等待他们完成工作。备菜师和洗菜师工作完之后，厨师开始炒菜。这个过程对于服务员来说其实是透明的，他不需要关心到底后厨是怎么做菜的。</span></p>
  <p data-lake-id="u17680c48" id="u17680c48"><br></p>
  <p data-lake-id="u0074e704" id="u0074e704"><img src="http://www.hollischuang.com/wp-content/uploads/2018/12/15442669374513.gif"><span data-lake-id="u20a354d5" id="u20a354d5">￼</span></p>
  <p data-lake-id="u008f78c9" id="u008f78c9"><br></p>
  <p data-lake-id="ud4789e0f" id="ud4789e0f"><span data-lake-id="u96a919c7" id="u96a919c7">由于各服务部署在不同机器上，要想在服务间进行远程调用免不了网络通信过程，服务消费方每调用一个服务都要写一坨网络通信相关的代码，不仅复杂而且极易出错。</span></p>
  <p data-lake-id="u7aea3c44" id="u7aea3c44"><br></p>
  <p data-lake-id="u12c4437d" id="u12c4437d"><span data-lake-id="u52268e03" id="u52268e03">如果有一种方式能让我们像调用本地服务一样调用远程服务，而让调用者对网络通信这些细节透明，那么将大大提高生产力，比如服务消费方在执行</span><code data-lake-id="u454dc8e6" id="u454dc8e6"><span data-lake-id="u1aa64e02" id="u1aa64e02">orderService.buy("HHKB键盘")</span></code><span data-lake-id="u96cc1760" id="u96cc1760">时，实质上调用的是远端的服务。这种方式其实就是RPC。而提供了这种功能的工具我们称之为RPC框架。</span></p>
  <p data-lake-id="ucff3eb4b" id="ucff3eb4b"><br></p>
  <p data-lake-id="u626d318f" id="u626d318f"><span data-lake-id="u2a3453a2" id="u2a3453a2">在RPC框架中主要有三个角色：Provider、Consumer和Registry。如下图所示：</span></p>
  <p data-lake-id="u1e66c064" id="u1e66c064"><br></p>
  <p data-lake-id="u433cca39" id="u433cca39"><img src="http://www.hollischuang.com/wp-content/uploads/2018/12/15442678994512.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_15%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u3b641c8a" id="u3b641c8a">​</span></p>
  <p data-lake-id="u48d68fae" id="u48d68fae"><br></p>
  <p data-lake-id="u35e9f23c" id="u35e9f23c"><span data-lake-id="ufc53c36c" id="ufc53c36c">Server: 暴露服务的服务提供方。 </span></p>
  <p data-lake-id="ufe1008c1" id="ufe1008c1"><span data-lake-id="u260faa9a" id="u260faa9a">Client: 调用远程服务的服务消费方。 </span></p>
  <p data-lake-id="ufb7648ee" id="ufb7648ee"><span data-lake-id="ufe15889f" id="ufe15889f">Registry: 服务注册与发现的注册中心。</span></p>
  <p data-lake-id="uae62abb1" id="uae62abb1"><br></p>
  <p data-lake-id="u2401622b" id="u2401622b"><span data-lake-id="u0b243b52" id="u0b243b52">服务提供方和服务消费方都比较好理解，就是后厨的洗菜师和厨师啦。厨师就是服务消费方，洗菜师就是服务提供方。厨师依赖洗菜师提供的服务。</span></p>
  <p data-lake-id="u98050a12" id="u98050a12"><br></p>
  <p data-lake-id="u9954c90a" id="u9954c90a"><span data-lake-id="u3d8a2637" id="u3d8a2637">服务注册中心又是个什么东西呢？</span></p>
  <p data-lake-id="ud8b6ed98" id="ud8b6ed98"><br></p>
  <p data-lake-id="ud8957bb9" id="ud8957bb9"><span data-lake-id="u5193d4aa" id="u5193d4aa">其实这个也比较好理解。对于那种很大的饭店来说，厨师可能有很多（集群部署），洗菜师也有很多（集群部署）。而厨师想要洗菜师帮忙洗菜的时候，他不会直接找某个洗菜师，而是通知一个中间人，这个人可能是洗菜师团队的领导，也可能就是一个专门协调后厨的人员。他知道整个厨房有多少洗菜师，也知道哪个洗菜师今天来上班了（需要先进行服务注册）。而且，他还可以根据各个洗菜师的忙碌情况动态分配任务（负载均衡）。</span></p>
  <p data-lake-id="u692fea77" id="u692fea77"><br></p>
  <p data-lake-id="ubeb95cda" id="ubeb95cda"><span data-lake-id="u5160424f" id="u5160424f">这个中间人就是服务注册中心。</span></p>
  <p data-lake-id="u7e2a3c5a" id="u7e2a3c5a"><br></p>
  <p data-lake-id="ua32a4c87" id="ua32a4c87"><img src="http://www.hollischuang.com/wp-content/uploads/2018/12/15442683038459.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_14%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u4d652c9a" id="u4d652c9a">￼</span></p>
  <p data-lake-id="u6ba8f849" id="u6ba8f849"><br></p>
  <p data-lake-id="ub1cd14ad" id="ub1cd14ad"><span data-lake-id="u14341bb3" id="u14341bb3">服务提供者启动后主动向注册中心注册机器ip、port以及提供的服务列表； 服务消费者启动时向注册中心获取服务提供方地址列表，可实现软负载均衡和Failover；</span></p>
  <p data-lake-id="uacbc0c04" id="uacbc0c04"><br></p>
  <h3 data-lake-id="bbb7879b" id="bbb7879b"><span data-lake-id="u3912529a" id="u3912529a">实现RPC需要用到的技术</span></h3>
  <p data-lake-id="u7817456c" id="u7817456c"><br></p>
  <p data-lake-id="u0a773c74" id="u0a773c74"><span data-lake-id="ud261e183" id="ud261e183">一个成熟的RPC框架需要考虑的问题有很多，这里只介绍实现一个远程调用需要用到的基本技术，感兴趣的朋友可以找一些开源的RPC框架代码来看下。</span></p>
  <p data-lake-id="ufd8137e8" id="ufd8137e8"><br></p>
  <p data-lake-id="ud5b749b0" id="ud5b749b0"><strong><span data-lake-id="u8aa66255" id="u8aa66255">动态代理</span></strong></p>
  <p data-lake-id="u4f0a6532" id="u4f0a6532"><br></p>
  <p data-lake-id="ue1299f24" id="ue1299f24"><span data-lake-id="uc9cd77bd" id="uc9cd77bd">生成 client stub和server stub需要用到 </span><strong><span data-lake-id="ud74ee95e" id="ud74ee95e">Java 动态代理技术 </span></strong><span data-lake-id="u6195b075" id="u6195b075">，我们可以使用JDK原生的动态代理机制，可以使用一些开源字节码工具框架 如：CgLib、Javassist等。</span></p>
  <p data-lake-id="u6b4adca9" id="u6b4adca9"><br></p>
  <p data-lake-id="u81b986e6" id="u81b986e6"><strong><span data-lake-id="ue5097a62" id="ue5097a62">序列化</span></strong><span data-lake-id="ub35bcfec" id="ub35bcfec"> 为了能在网络上传输和接收 Java对象，我们需要对它进行序列化和反序列化操作。</span></p>
  <p data-lake-id="ubefd0ccc" id="ubefd0ccc"><br></p>
  <p data-lake-id="ua775f16f" id="ua775f16f"><span data-lake-id="uf1b85180" id="uf1b85180">可以使用Java原生的序列化机制，但是效率非常低，推荐使用一些开源的、成熟的序列化技术，例如：protobuf、Thrift、hessian、Kryo、Msgpack</span></p>
  <p data-lake-id="uaec2d15d" id="uaec2d15d"><br></p>
  <p data-lake-id="ud462ca7c" id="ud462ca7c"><strong><span data-lake-id="uf22ef952" id="uf22ef952">NIO</span></strong></p>
  <p data-lake-id="u760d6b1e" id="u760d6b1e"><br></p>
  <p data-lake-id="ubcb629d4" id="ubcb629d4"><span data-lake-id="u526ff95a" id="u526ff95a">当前很多RPC框架都直接基于netty这一IO通信框架，比如阿里巴巴的HSF、dubbo，Hadoop Avro，推荐使用Netty 作为底层通信框架。</span></p>
  <p data-lake-id="u7ff5a8a7" id="u7ff5a8a7"><br></p>
  <p data-lake-id="u44c6bc57" id="u44c6bc57"><strong><span data-lake-id="u854f0ecd" id="u854f0ecd">服务注册中心</span></strong><span data-lake-id="uac73ca9b" id="uac73ca9b"> 可选技术： Redis、Zookeeper、Consul、Etcd</span></p>
  <p data-lake-id="ue3308db8" id="ue3308db8"><br></p>
  <h3 data-lake-id="2f9e4d91" id="2f9e4d91"><span data-lake-id="u13795bcd" id="u13795bcd">开源RPC框架</span></h3>
  <p data-lake-id="u0cbfe7f6" id="u0cbfe7f6"><br></p>
  <p data-lake-id="u3f2f84ed" id="u3f2f84ed"><strong><span data-lake-id="u12ffc73c" id="u12ffc73c">Dubbo</span></strong></p>
  <p data-lake-id="u448a3815" id="u448a3815"><br></p>
  <p data-lake-id="u53bb0999" id="u53bb0999"><span data-lake-id="u26d39885" id="u26d39885">Dubbo 是阿里巴巴公司开源的一个Java高性能优秀的服务框架，使得应用可通过高性能的 RPC 实现服务的输出和输入功能，可以和 Spring框架无缝集成。目前已经进入Apache孵化器。</span></p>
  <p data-lake-id="u96b59022" id="u96b59022"><br></p>
  <p data-lake-id="u9e93c549" id="u9e93c549"><strong><span data-lake-id="u934e718e" id="u934e718e">Motan</span></strong></p>
  <p data-lake-id="ub79e0761" id="ub79e0761"><br></p>
  <p data-lake-id="u3fb66b8e" id="u3fb66b8e"><span data-lake-id="u963bfccc" id="u963bfccc">Motan是新浪微博开源的一个Java RPC框架。2016年5月开源。Motan 在微博平台中已经广泛应用，每天为数百个服务完成近千亿次的调用。</span></p>
  <p data-lake-id="u367a1ddb" id="u367a1ddb"><br></p>
  <p data-lake-id="u1cc3c9ef" id="u1cc3c9ef"><strong><span data-lake-id="u547e757e" id="u547e757e">gRPC</span></strong></p>
  <p data-lake-id="u01756cd2" id="u01756cd2"><br></p>
  <p data-lake-id="u4061ec4d" id="u4061ec4d"><span data-lake-id="u1091b5ff" id="u1091b5ff">gRPC是Google开发的高性能、通用的开源RPC框架，其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计，基于ProtoBuf(Protocol Buffers)序列化协议开发，且支持众多开发语言。本身它不是分布式的，所以要实现上面的框架的功能需要进一步的开发。</span></p>
  <p data-lake-id="uda340e02" id="uda340e02"><br></p>
  <p data-lake-id="u5cfb3518" id="u5cfb3518"><strong><span data-lake-id="u3ee22f8c" id="u3ee22f8c">thrift</span></strong></p>
  <p data-lake-id="uac07aea1" id="uac07aea1"><br></p>
  <p data-lake-id="ub0554511" id="ub0554511"><span data-lake-id="u8d0b14c6" id="u8d0b14c6">thrift是Apache的一个跨语言的高性能的服务框架，也得到了广泛的应用。</span></p>
  <p data-lake-id="uba42c602" id="uba42c602"><br></p>
 </body>
</html>